package org.mybatis.generator.plugin.ext;

import org.mybatis.generator.api.*;
import org.mybatis.generator.api.dom.java.*;
import org.mybatis.generator.exception.ShellException;
import org.mybatis.generator.internal.DefaultShellCallback;
import org.mybatis.generator.internal.util.StringUtility;
import org.mybatis.generator.model.TableClass;
import org.mybatis.generator.model.TableColumnBuilder;

import java.io.File;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * Mapper.java文件生成插件
 * @author 孤傲苍狼
 *
 */
public class MapperGeneratorPlugin extends PluginAdapter {

    //BaseMapper所在的包
    private String baseMapperPack;
    private ShellCallback shellCallback = null;
    private String targetProject;
    private String daoTargetPackage;
    private String generateMode;//生成模式，0：原始模式，1：使用BaseMapper模式

    private FullyQualifiedJavaType pojoType;
    private FullyQualifiedJavaType pojoCriteriaType;
    private String pojoPackage;

    public MapperGeneratorPlugin(){
        shellCallback = new DefaultShellCallback(false);
    }

    public boolean validate(List<String> warnings) {
        baseMapperPack = properties.getProperty("baseMapperPack");
        daoTargetPackage = properties.getProperty("daoTargetPackage");
        targetProject = properties.getProperty("targetProject");
        generateMode = properties.getProperty("generateMode");
        pojoPackage = context.getJavaModelGeneratorConfiguration().getTargetPackage();
        if(!StringUtility.stringHasValue(generateMode)){
            generateMode = "1";//默认使用BaseMapper模式
        }
        return true;
    }

    /**
     * 生成xxxMapper.java
     */
    @Override
    public boolean clientGenerated(Interface interfaze,
                                   TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
        System.out.println("MapperGeneratorPlugin→clientGenerated");
        List<IntrospectedColumn> primaryKeyColumns = introspectedTable.getPrimaryKeyColumns();
        if(generateMode.equals("0") || primaryKeyColumns.size()>1){//primaryKeyColumns.size()>1表示是表使用的联合主键
            System.out.println("采用原始模式生成");
            TableClass tableClass = TableColumnBuilder.build(introspectedTable);
            String tableName = tableClass.getTableName();
            JavaFormatter javaFormatter = context.getJavaFormatter();
            GeneratedJavaFile mapperJavafile = new GeneratedJavaFile(interfaze, targetProject, javaFormatter);
            try {
                File mapperDir = shellCallback.getDirectory(targetProject, daoTargetPackage);
                File mapperFile = new File(mapperDir, mapperJavafile.getFileName());
                // 文件不存在
                if (!mapperFile.exists()) {
                    System.out.println("生成"+mapperJavafile.getFileName());
                    return true;//返回true就生成xxxMapper.java
                }else {
                    System.out.println(tableName+"表的Mapper.java已经存在，不再生成");
                    //已经存在就不生成
                    return false;
                }
            } catch (ShellException e) {
                e.printStackTrace();
                return true;
            }
        }
        return false; //返回false就不生成xxxMapper.java
    }

    /**
     * 生成xxxMapper.java
     */
    @Override
    public List<GeneratedJavaFile> contextGenerateAdditionalJavaFiles(IntrospectedTable introspectedTable) {
        System.out.println("MapperGeneratorPlugin→contextGenerateAdditionalJavaFiles");
        //需要生成的java文件集合
        List<GeneratedJavaFile> mapperJavaFiles = new ArrayList<>();
        if (generateMode.equals("1")) {//使用BaseMapper模式
            JavaFormatter javaFormatter = context.getJavaFormatter();
            FullyQualifiedJavaType pkType = null;
            boolean isCompositePK = false;//是否为复合主键
            TableClass tableClass = TableColumnBuilder.build(introspectedTable);
            String tableName = tableClass.getTableName();

            pojoType = new FullyQualifiedJavaType(pojoPackage + "." + tableClass.getShortClassName());
            pojoCriteriaType = new FullyQualifiedJavaType(pojoPackage + "." + tableClass.getShortClassName()+"Example");

            List<IntrospectedColumn> primaryKeyColumns = introspectedTable.getPrimaryKeyColumns();
            if (primaryKeyColumns.isEmpty()) {//如果表没有主键，默认使用字符串类型作为主键
                pkType = new FullyQualifiedJavaType("java.lang.String");
            } else {
                if (primaryKeyColumns.size() == 1) {//只有一个主键
                    System.out.println(tableName + "表只有一个主键");
                    IntrospectedColumn column = primaryKeyColumns.get(0);
                    System.out.println(tableName + "表的" + column.getActualColumnName() + "列是主键");
                    pkType = column.getFullyQualifiedJavaType();
                } else {
                    //联合主键
                    isCompositePK = true;
                }
            }
            for (GeneratedJavaFile javaFile : introspectedTable.getGeneratedJavaFiles()) {
                CompilationUnit unit = javaFile.getCompilationUnit();
                FullyQualifiedJavaType javaType = unit.getType();
                String shortName = javaType.getShortName();
                if (shortName.endsWith("Example")) {// 针对Example类不要生成Mapper
                    continue;
                }

                String modelType = javaType.getShortName();
                String modelExampleType = modelType + "Example";
                Interface mapperInterface = new Interface(daoTargetPackage + "." + shortName + "Mapper");
                mapperInterface.setVisibility(JavaVisibility.PUBLIC);
                mapperInterface.addJavaDocLine(" /**");
                mapperInterface.addJavaDocLine(" * 由MBG工具自动生成，添加与扩展XML文件中对应的同名方法");
                mapperInterface.addJavaDocLine(" * 文件生成时间：" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
                mapperInterface.addJavaDocLine(" **/");

                FullyQualifiedJavaType modelJavaType = new FullyQualifiedJavaType(javaType.getPackageName() + "." + modelType);
                mapperInterface.addImportedType(modelJavaType);//导入model包
                FullyQualifiedJavaType modelExampleJavaType = new FullyQualifiedJavaType(javaType.getPackageName() + "." + modelExampleType);
                mapperInterface.addImportedType(modelExampleJavaType);//导入modelExample包
                if (!isCompositePK) {//如果不是复合主键就生成继承了BaseMapper的xxxMapper.java
                    FullyQualifiedJavaType fqjt = new FullyQualifiedJavaType("BaseMapper<"
                            + introspectedTable.getBaseRecordType() + ","
                            + introspectedTable.getExampleType() + ","
                            + pkType + ">");
                    FullyQualifiedJavaType baseMapperJavaType = new FullyQualifiedJavaType(baseMapperPack + ".BaseMapper");
                    mapperInterface.addImportedType(baseMapperJavaType);//导入BaseMapper
                    mapperInterface.addSuperInterface(fqjt);//添加继承父类

                    //表里面有大文本字段，生成xxxWithBLOBs的相关方法
                    if (introspectedTable.hasBLOBColumns()) {
                        //导入List包
                        mapperInterface.addImportedType(new FullyQualifiedJavaType("java.util.List"));
                        Method method = selectByExampleWithBLOBs(shortName);
                        mapperInterface.addMethod(method);
                        method = addMethod("updateByPrimaryKeyWithBLOBs",introspectedTable, 1);
                        mapperInterface.addMethod(method);
                        method = addMethod("updateByExampleWithBLOBs",introspectedTable, 4);
                        mapperInterface.addMethod(method);
                    }
                    try {
                        GeneratedJavaFile mapperJavafile = new GeneratedJavaFile(mapperInterface, targetProject, javaFormatter);
                        File mapperDir = shellCallback.getDirectory(targetProject, daoTargetPackage);
                        File mapperFile = new File(mapperDir, mapperJavafile.getFileName());
                        // 文件不存在
                        if (!mapperFile.exists()) {
                            mapperJavaFiles.add(mapperJavafile);
                        }
                    } catch (ShellException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        return mapperJavaFiles;
    }

    /**
     * 添加方法
     *
     */
    private Method addMethod(String methodName, IntrospectedTable introspectedTable,int type) {
        Method method = new Method();
        method.setName(methodName);
        method.setReturnType(FullyQualifiedJavaType.getIntInstance());
        addParams(introspectedTable, method, type);
        method.setVisibility(JavaVisibility.PUBLIC);
        return method;
    }

    /**
     * 生成selectByExampleWithBLOBs方法
     *
     */
    protected Method  selectByExampleWithBLOBs(String tableName) {
        Method method = new Method();
        method.setName("selectByExampleWithBLOBs");
        method.setReturnType(new FullyQualifiedJavaType("List<" + tableName + ">"));
        method.addParameter(new Parameter(pojoCriteriaType, "example"));
        method.setVisibility(JavaVisibility.PUBLIC);
        return method;
    }

    /**
     * type 的意义 pojo 1 key 2 example 3 pojo+example 4
     */
    protected String addParams(IntrospectedTable introspectedTable, Method method, int type1) {
        switch (type1) {
            case 1:
                method.addParameter(new Parameter(pojoType, "record"));
                return "record";
            case 2:
                if (introspectedTable.getRules().generatePrimaryKeyClass()) {
                    FullyQualifiedJavaType type = new FullyQualifiedJavaType(introspectedTable.getPrimaryKeyType());
                    method.addParameter(new Parameter(type, "key"));
                } else {
                    for (IntrospectedColumn introspectedColumn : introspectedTable.getPrimaryKeyColumns()) {
                        FullyQualifiedJavaType type = introspectedColumn.getFullyQualifiedJavaType();
                        method.addParameter(new Parameter(type, introspectedColumn.getJavaProperty()));
                    }
                }
                StringBuffer sb = new StringBuffer();
                for (IntrospectedColumn introspectedColumn : introspectedTable.getPrimaryKeyColumns()) {
                    sb.append(introspectedColumn.getJavaProperty());
                    sb.append(",");
                }
                if(sb.length()>0){
                    sb.setLength(sb.length() - 1);
                }
                return sb.toString();
            case 3:
                method.addParameter(new Parameter(pojoCriteriaType, "example"));
                return "example";
            case 4:
                method.addParameter(0, new Parameter(pojoType, "record"));
                method.addParameter(1, new Parameter(pojoCriteriaType, "example"));
                return "record, example";
            default:
                break;
        }
        return null;
    }
}